﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using JEng_Mark_1.GameComponents;
using Microsoft.Xna.Framework.Graphics;
using JEng_Mark_1.Textures;
using System.Diagnostics;


namespace JEng_Mark_1.Managers
{
    public class JScreenManager : DrawableGameComponent
    {
        private static List<JGameScreen> _screens = new List<JGameScreen>();
        private static List<JGameScreen> _screensToUpdate = new List<JGameScreen>();

        /// <summary>
        /// Expose an array holding all the screens. We return a copy
        /// rather than the real master list.
        /// </summary>
        /// <returns>A copy of all the current game screens</returns>
        public static JGameScreen[] GetScreens()
        {
            return _screens.ToArray();
        }

        private static bool _initialized = false;

        /// <summary>
        /// Is the JSceneManager initialized
        /// </summary>
        public static bool Initialized { get { return _initialized; } }

        private static SpriteBatch _spriteBatch;

        /// <summary>
        /// A default SpriteBatch shared by all screen. This consolidates drawing
        /// </summary>
        public static SpriteBatch SpriteBatch { get { return _spriteBatch; } }

        private static SpriteFont _font;

        /// <summary>
        /// A default font shared by all screens
        /// </summary>
        public static SpriteFont Font { get { return _font; } }

        private bool _traceEnabled = true;

        /// <summary>
        /// If true, the manager prints out a list of all the current screens each time it is updated.
        /// This can be useful for making sure everything is being added and removed at the right times.
        /// </summary>
        public bool TraceEnabled { get { return _traceEnabled; } set { _traceEnabled = value; } }

        /// <summary>
        /// Construct a new JScreenManager
        /// </summary>
        /// <param name="game">The XNA game object</param>
        public JScreenManager(Game game)
            : base(game)
        {
            Enabled = true;
        }

        /// <summary>
        /// Load content shared across game screens, and for each screen already added
        /// </summary>
        protected override void LoadContent()
        {
            base.LoadContent();

            _spriteBatch = new SpriteBatch(JEngineManager.Device);
            _font = JEngineManager.ContentManager.Load<SpriteFont>("Content/Fonts/default");
            JTextureManager.AddTexture(new JTexture("Content/Textures/blank"), "blank");

            foreach (JGameScreen screen in _screens)
            {
                screen.LoadContent();
            }
        }

        /// <summary>
        /// Tell each screen to unload its content
        /// </summary>
        protected override void UnloadContent()
        {
            base.UnloadContent();

            foreach (JGameScreen screen in _screens)
            {
                screen.UnloadContent();
            }
        }

        /// <summary>
        /// Initialize the screen manager
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            _initialized = true;
        }

        /// <summary>
        /// Update each screen on the master list
        /// </summary>
        /// <param name="gameTime">Timing snapshot</param>
        public override void Update(GameTime gameTime)
        {
            //Read the keyboard and gamepad states
            JEngineManager.Input.Update();

            //make a copy of the master screen list, to avoid confusion if the process of updating one screen
            //adds or removes others.
            _screensToUpdate.Clear();

            foreach (JGameScreen screen in _screens)
            {
                _screensToUpdate.Add(screen);
            }

            bool otherScreenHasFocus = !Game.IsActive;
            bool coveredByOtherScreen = false;

            //Loop as long as their are screens waiting to be updated
            while (_screensToUpdate.Count > 0)
            {
                //Pop off the topmost screen
                JGameScreen screen = _screensToUpdate[_screensToUpdate.Count - 1];
                _screensToUpdate.RemoveAt(_screensToUpdate.Count - 1);

                //update the screen
                screen.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

                if (screen.ScreenState == ScreenState.TransitionOn || screen.ScreenState == ScreenState.Active)
                {
                    //If this is the first active screen we came across, give it a chance to handle input
                    if (!otherScreenHasFocus)
                    {
                        screen.HandleInput(JEngineManager.Input);
                        otherScreenHasFocus = true;
                    }

                    //If this is an active non-popup, inform any subsequent screens that they are covered by it.
                    if (!screen.IsPopup)
                        coveredByOtherScreen = true;
                }
            }

            //Print debug trace?
            if (_traceEnabled)
                TraceScreens();
        }

        /// <summary>
        /// Prints a list of all the screens, for debugging
        /// </summary>
        private void TraceScreens()
        {
            List<string> screenNames = new List<string>();

            foreach (JGameScreen screen in _screens)
                screenNames.Add(screen.GetType().Name);

            Trace.WriteLine(string.Join(", ", screenNames.ToArray()));
        }

        /// <summary>
        /// Make each current screen draw itself
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Draw(GameTime gameTime)
        {
            foreach (JGameScreen screen in _screens)
            {
                if (screen.ScreenState == ScreenState.Hidden)
                    continue;

                screen.Draw(gameTime);
            }
            foreach (JGameScreen screen in _screens)
            {
                if (screen.ScreenState == ScreenState.Hidden)
                    continue;

                screen.PostUIDraw(gameTime);
            }
        }

        /// <summary>
        /// Adds a new screen to the manager
        /// </summary>
        /// <param name="screen">Screen to add</param>
        public static void AddScreen(JGameScreen screen)
        {
            //If we have a graphics device, tell the screen to load content aswell.
            _screens.Add(screen);
            JLog.addMessage("Added new screen " + screen.ToString());
            if (_initialized)
            {
                screen.LoadContent();
            }
        }
        /// <summary>
        /// Removes a screen from the screen manager. You should normally use JGameScreen.ExitScreen instead of
        /// calling this directly, so the screen can gradually transition off rather than just being instantly
        /// removed.
        /// </summary>
        /// <param name="screen">JGameScreen to remove</param>
        public static void RemoveScreen(JGameScreen screen)
        {
            //If we have a graphics device, tell the screen to unload content aswell
            if (_initialized)
            {
                screen.UnloadContent();
            }

            _screens.Remove(screen);
            _screensToUpdate.Remove(screen);
        }

        public static void FadeBackBufferToBlack(int alpha)
        {
            Viewport viewport = JEngineManager.Device.Viewport;

            _spriteBatch.Begin();
            _spriteBatch.Draw(JTextureManager.GetTexture("blank").BaseTexture as Texture2D,
                new Rectangle(0, 0, viewport.Width, viewport.Height),
                new Color(0, 0, 0, (byte)alpha));
            _spriteBatch.End();
        }
    }
}
